<?php
/*
Copyright (c) 2009 GPA Software, Inc.

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
/* ! REQUIREMENTS */
// CURL MODULE
// mbstring support

// ! start timer
$__mtime = microtime();
$__mtime = explode(' ', $__mtime);
$__mtime = $__mtime[1] + $__mtime[0];
$__starttime = $__mtime;

// ! cloud files libraries: included version: v1.3.0. Cloud Files libraries is copyrighted by Mosso.
/* START CLOUD FILES LIBRARIES */

 class SyntaxException extends Exception { } class AuthenticationException extends Exception { } class InvalidResponseException extends Exception { } class NonEmptyContainerException extends Exception { } class NoSuchObjectException extends Exception { } class NoSuchContainerException extends Exception { } class NoSuchAccountException extends Exception { } class MisMatchedChecksumException extends Exception { } class IOException extends Exception { } class CDNNotEnabledException extends Exception { }

 define("PHP_CF_VERSION", "1.3.0"); define("USER_AGENT", sprintf("PHP-CloudFiles/%s", PHP_CF_VERSION)); define("ACCOUNT_CONTAINER_COUNT", "X-Account-Container-Count"); define("ACCOUNT_BYTES_USED", "X-Account-Bytes-Used"); define("CONTAINER_OBJ_COUNT", "X-Container-Object-Count"); define("CONTAINER_BYTES_USED", "X-Container-Bytes-Used"); define("METADATA_HEADER", "X-Object-Meta-"); define("CDN_URI", "X-CDN-URI"); define("CDN_ENABLED", "X-CDN-Enabled"); define("CDN_TTL", "X-TTL"); define("CDNM_URL", "X-CDN-Management-Url"); define("STORAGE_URL", "X-Storage-Url"); define("AUTH_TOKEN", "X-Auth-Token"); define("AUTH_USER_HEADER", "X-Auth-User"); define("AUTH_KEY_HEADER", "X-Auth-Key"); define("AUTH_USER_HEADER_LEGACY", "X-Storage-User"); define("AUTH_KEY_HEADER_LEGACY", "X-Storage-Pass"); define("AUTH_TOKEN_LEGACY", "X-Storage-Token"); class CF_Http { private $error_str; private $dbug; private $cabundle_path; private $api_version; private $storage_url; private $cdnm_url; private $auth_token; private $response_status; private $response_reason; private $connections; private $_user_read_progress_callback_func; private $_user_write_progress_callback_func; private $_write_callback_type; private $_text_list; private $_account_container_count; private $_account_bytes_used; private $_container_object_count; private $_container_bytes_used; private $_obj_etag; private $_obj_last_modified; private $_obj_content_type; private $_obj_content_length; private $_obj_metadata; private $_obj_write_resource; private $_obj_write_string; private $_cdn_enabled; private $_cdn_uri; private $_cdn_ttl; function __construct($api_version) { $this->dbug = False; $this->cabundle_path = NULL; $this->api_version = $api_version; $this->error_str = NULL; $this->storage_url = NULL; $this->cdnm_url = NULL; $this->auth_token = NULL; $this->response_status = NULL; $this->response_reason = NULL; $this->connections = array( "GET_CALL" => NULL, "PUT_OBJ" => NULL, "HEAD" => NULL, "PUT_CONT" => NULL, "DEL_POST" => NULL, ); $this->_user_read_progress_callback_func = NULL; $this->_user_write_progress_callback_func = NULL; $this->_write_callback_type = NULL; $this->_text_list = array(); $this->_account_container_count = 0; $this->_account_bytes_used = 0; $this->_container_object_count = 0; $this->_container_bytes_used = 0; $this->_obj_write_resource = NULL; $this->_obj_write_string = ""; $this->_obj_etag = NULL; $this->_obj_last_modified = NULL; $this->_obj_content_type = NULL; $this->_obj_content_length = NULL; $this->_obj_metadata = array(); $this->_cdn_enabled = NULL; $this->_cdn_uri = NULL; $this->_cdn_ttl = NULL; } function ssl_use_cabundle($path=NULL) { if ($path) { $this->cabundle_path = $path; } else { $this->cabundle_path = dirname(__FILE__) . "/share/cacert.pem"; } if (!file_exists($this->cabundle_path)) { throw new IOException("Could not use CA bundle: " . $this->cabundle_path); } return; } private function _set_connections_cainfo() { if ($this->cabundle_path) { foreach ($this->connections as $k => $v) { if (!is_null($v)) { curl_setopt($this->connections[$k], CURLOPT_SSL_VERIFYPEER, True); curl_setopt($this->connections[$k], CURLOPT_CAINFO, $this->cabundle_path); } } } return; } function authenticate($user, $pass, $acct=NULL, $host=NULL) { $path = array(); if (isset($acct) || isset($host)) { $headers = array( sprintf("%s: %s", AUTH_USER_HEADER_LEGACY, $user), sprintf("%s: %s", AUTH_KEY_HEADER_LEGACY, $pass), ); $path[] = $host; $path[] = rawurlencode(sprintf("v%d",$this->api_version)); $path[] = rawurlencode($acct); } else { $headers = array( sprintf("%s: %s", AUTH_USER_HEADER, $user), sprintf("%s: %s", AUTH_KEY_HEADER, $pass), ); $path[] = "https://api.mosso.com"; } $path[] = "auth"; $url = implode("/", $path); $curl_ch = curl_init(); if ($this->cabundle_path) { curl_setopt($curl_ch, CURLOPT_SSL_VERIFYPEER, True); curl_setopt($curl_ch, CURLOPT_CAINFO, $this->cabundle_path); } curl_setopt($curl_ch, CURLOPT_VERBOSE, $this->dbug); curl_setopt($curl_ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curl_ch, CURLOPT_MAXREDIRS, 4); curl_setopt($curl_ch, CURLOPT_HEADER, 0); curl_setopt($curl_ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl_ch, CURLOPT_USERAGENT, USER_AGENT); curl_setopt($curl_ch, CURLOPT_HEADERFUNCTION,array(&$this,'_auth_hdr_cb')); curl_setopt($curl_ch, CURLOPT_URL, $url); curl_exec($curl_ch); curl_close($curl_ch); return array($this->response_status, $this->response_reason, $this->storage_url, $this->cdnm_url, $this->auth_token); } function list_cdn_containers() { $conn_type = "GET_CALL"; $url_path = $this->_make_path("CDN", $container_name); $this->_write_callback_type = "TEXT_LIST"; $return_code = $this->_send_request($conn_type, $url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; array(0,$this->error_str,array()); } if ($return_code == 401) { return array($return_code,"Unauthorized",array()); } if ($return_code == 404) { return array($return_code,"Account not found.",array()); } if ($return_code == 204) { return array($return_code,"Account has no CDN enabled Containers.", array()); } if ($return_code == 200) { return array($return_code,$this->response_reason,$this->_text_list); } $this->error_str = "Unexpected HTTP response: ".$this->response_reason; return array($return_code,$this->error_str,array()); } function update_cdn_container($container_name, $ttl=86400) { if (!$container_name) { throw new SyntaxException("Container name not set."); } $url_path = $this->_make_path("CDN", $container_name); $hdrs = array( CDN_ENABLED => "True", CDN_TTL => $ttl, ); $return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST"); if ($return_code == 401) { $this->error_str = "Unauthorized"; return array($return_code, $this->error_str, NULL); } if ($return_code == 404) { $this->error_str = "Container not found."; return array($return_code, $this->error_str, NULL); } if ($return_code != 202) { $this->error_str="Unexpected HTTP response: ".$this->response_reason; return array($return_code, $this->error_str, NULL); } return array($return_code, "Accepted", $this->_cdn_uri); } function add_cdn_container($container_name, $ttl=86400) { if (!$container_name) { throw new SyntaxException("Container name not set."); } $url_path = $this->_make_path("CDN", $container_name); $hdrs = array( CDN_ENABLED => "True", CDN_TTL => $ttl, ); $return_code = $this->_send_request("PUT_CONT", $url_path, $hdrs); if ($return_code == 401) { $this->error_str = "Unauthorized"; return array($return_code,$this->response_reason,False); } if (!in_array($return_code, array(201,202))) { $this->error_str="Unexpected HTTP response: ".$this->response_reason; return array($return_code,$this->response_reason,False); } return array($return_code,$this->response_reason,$this->_cdn_uri); } function remove_cdn_container($container_name) { if (!$container_name) { throw new SyntaxException("Container name not set."); } $url_path = $this->_make_path("CDN", $container_name); $hdrs = array(CDN_ENABLED => "False"); $return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST"); if ($return_code == 401) { $this->error_str = "Unauthorized"; return array($return_code, $this->error_str); } if ($return_code == 404) { $this->error_str = "Container not found."; return array($return_code, $this->error_str); } if ($return_code != 202) { $this->error_str="Unexpected HTTP response: ".$this->response_reason; return array($return_code, $this->error_str); } return array($return_code, "Accepted"); } function head_cdn_container($container_name) { if (!$container_name) { throw new SyntaxException("Container name not set."); } $conn_type = "HEAD"; $url_path = $this->_make_path("CDN", $container_name); $return_code = $this->_send_request($conn_type, $url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; array(0,$this->error_str,NULL,NULL,NULL); } if ($return_code == 401) { return array($return_code,"Unauthorized",NULL,NULL,NULL); } if ($return_code == 404) { return array($return_code,"Account not found.",NULL,NULL,NULL); } if ($return_code == 204) { return array($return_code,$this->response_reason, $this->_cdn_enabled, $this->_cdn_uri, $this->_cdn_ttl); } return array($return_code,$this->response_reason,NULL,NULL,NULL); } function list_containers($limit=0, $marker=NULL) { $conn_type = "GET_CALL"; $url_path = $this->_make_path(); $limit = intval($limit); $params = array(); if ($limit > 0) { $params[] = "limit=$limit"; } if ($marker) { $params[] = "marker=".rawurlencode($marker); } if (!empty($params)) { $url_path .= "?" . implode("&", $params); } $this->_write_callback_type = "TEXT_LIST"; $return_code = $this->_send_request($conn_type, $url_path); if (!$return_code) { $this->error_str = "Failed to obtain valid HTTP response."; return array(0,$this->error_str,array()); } if ($return_code == 204) { return array($return_code, "Account has no containers.", array()); } if ($return_code == 404) { $this->error_str = "Invalid account name for authentication token."; return array($return_code,$this->error_str,array()); } if ($return_code == 200) { return array($return_code, $this->response_reason, $this->_text_list); } $this->error_str = "Unexpected HTTP response: ".$this->response_reason; return array($return_code,$this->error_str,array()); } function list_containers_info($limit=0, $marker=NULL) { $conn_type = "GET_CALL"; $url_path = $this->_make_path() . "?format=json"; $limit = intval($limit); $params = array(); if ($limit > 0) { $params[] = "limit=$limit"; } if ($marker) { $params[] = "marker=".rawurlencode($marker); } if (!empty($params)) { $url_path .= "&" . implode("&", $params); } $this->_write_callback_type = "OBJECT_STRING"; $return_code = $this->_send_request($conn_type, $url_path); if (!$return_code) { $this->error_str = "Failed to obtain valid HTTP response."; return array(0,$this->error_str,array()); } if ($return_code == 204) { return array($return_code, "Account has no containers.", array()); } if ($return_code == 404) { $this->error_str = "Invalid account name for authentication token."; return array($return_code,$this->error_str,array()); } if ($return_code == 200) { $json_body = json_decode($this->_obj_write_string, True); return array($return_code, $this->response_reason, $json_body); } $this->error_str = "Unexpected HTTP response: ".$this->response_reason; return array($return_code,$this->error_str,array()); } function head_account() { $conn_type = "HEAD"; $url_path = $this->_make_path(); $return_code = $this->_send_request($conn_type,$url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; array(0,$this->error_str,0,0); } if ($return_code == 404) { return array($return_code,"Account not found.",0,0); } if ($return_code == 204) { return array($return_code,$this->response_reason, $this->_account_container_count, $this->_account_bytes_used); } return array($return_code,$this->response_reason,0,0); } function create_container($container_name) { if (!$container_name) { throw new SyntaxException("Container name not set."); } $url_path = $this->_make_path("STORAGE", $container_name); $return_code = $this->_send_request("PUT_CONT",$url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return False; } return $return_code; } function delete_container($container_name) { if (!$container_name) { throw new SyntaxException("Container name not set."); } $url_path = $this->_make_path("STORAGE", $container_name); $return_code = $this->_send_request("DEL_POST",$url_path,array(),"DELETE"); if (!$return_code) { $this->error_str = "Failed to obtain http response"; } if ($return_code == 409) { $this->error_str = "Container must be empty prior to removing it."; } if ($return_code == 404) { $this->error_str = "Specified container did not exist to delete."; } if ($return_code != 204) { $this->error_str = "Unexpected HTTP return code: $return_code."; } return $return_code; } function list_objects($cname,$limit=0,$marker=NULL,$prefix=NULL,$path=NULL) { if (!$cname) { $this->error_str = "Container name not set."; return array(0, $this->error_str, array()); } $url_path = $this->_make_path("STORAGE", $cname); $limit = intval($limit); $params = array(); if ($limit > 0) { $params[] = "limit=$limit"; } if ($marker) { $params[] = "marker=".rawurlencode($marker); } if ($prefix) { $params[] = "prefix=".rawurlencode($prefix); } if ($path) { $params[] = "path=".rawurlencode($path); } if (!empty($params)) { $url_path .= "?" . implode("&", $params); } $conn_type = "GET_CALL"; $this->_write_callback_type = "TEXT_LIST"; $return_code = $this->_send_request($conn_type,$url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return array(0,$this->error_str,array()); } if ($return_code == 204) { $this->error_str = "Container has no Objects."; return array($return_code,$this->error_str,array()); } if ($return_code == 404) { $this->error_str = "Container has no Objects."; return array($return_code,$this->error_str,array()); } if ($return_code == 200) { return array($return_code,$this->response_reason, $this->_text_list); } $this->error_str = "Unexpected HTTP response code: $return_code"; return array(0,$this->error_str,array()); } function get_objects($cname,$limit=0,$marker=NULL,$prefix=NULL,$path=NULL) { if (!$cname) { $this->error_str = "Container name not set."; return array(0, $this->error_str, array()); } $url_path = $this->_make_path("STORAGE", $cname); $limit = intval($limit); $params = array(); $params[] = "format=json"; if ($limit > 0) { $params[] = "limit=$limit"; } if ($marker) { $params[] = "marker=".rawurlencode($marker); } if ($prefix) { $params[] = "prefix=".rawurlencode($prefix); } if ($path) { $params[] = "path=".rawurlencode($path); } if (!empty($params)) { $url_path .= "?" . implode("&", $params); } $conn_type = "GET_CALL"; $this->_write_callback_type = "OBJECT_STRING"; $return_code = $this->_send_request($conn_type,$url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return array(0,$this->error_str,array()); } if ($return_code == 204) { $this->error_str = "Container has no Objects."; return array($return_code,$this->error_str,array()); } if ($return_code == 404) { $this->error_str = "Container has no Objects."; return array($return_code,$this->error_str,array()); } if ($return_code == 200) { $json_body = json_decode($this->_obj_write_string, True); return array($return_code,$this->response_reason, $json_body); } $this->error_str = "Unexpected HTTP response code: $return_code"; return array(0,$this->error_str,array()); } function head_container($container_name) { if (!$container_name) { $this->error_str = "Container name not set."; return False; } $conn_type = "HEAD"; $url_path = $this->_make_path("STORAGE", $container_name); $return_code = $this->_send_request($conn_type,$url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; array(0,$this->error_str,0,0); } if ($return_code == 404) { return array($return_code,"Container not found.",0,0); } if ($return_code == 204) { return array($return_code,$this->response_reason, $this->_container_object_count, $this->_container_bytes_used); } return array($return_code,$this->response_reason,0,0); } function get_object_to_string(&$obj, $hdrs=array()) { if (!is_object($obj) || get_class($obj) != "CF_Object") { throw new SyntaxException( "Method argument is not a valid CF_Object."); } $conn_type = "GET_CALL"; $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name); $this->_write_callback_type = "OBJECT_STRING"; $return_code = $this->_send_request($conn_type,$url_path,$hdrs); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return array($return_code0,$this->error_str,NULL); } if ($return_code == 404) { $this->error_str = "Object not found."; return array($return_code0,$this->error_str,NULL); } if (($return_code < 200) || ($return_code > 299 && $return_code != 412 && $return_code != 304)) { $this->error_str = "Unexpected HTTP return code: $return_code"; return array($return_code,$this->error_str,NULL); } return array($return_code,$this->response_reason, $this->_obj_write_string); } function get_object_to_stream(&$obj, &$resource=NULL, $hdrs=array()) { if (!is_object($obj) || get_class($obj) != "CF_Object") { throw new SyntaxException( "Method argument is not a valid CF_Object."); } if (!is_resource($resource)) { throw new SyntaxException( "Resource argument not a valid PHP resource."); } $conn_type = "GET_CALL"; $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name); $this->_obj_write_resource = $resource; $this->_write_callback_type = "OBJECT_STREAM"; $return_code = $this->_send_request($conn_type,$url_path,$hdrs); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return array($return_code,$this->error_str); } if ($return_code == 404) { $this->error_str = "Object not found."; return array($return_code,$this->error_str); } if (($return_code < 200) || ($return_code > 299 && $return_code != 412 && $return_code != 304)) { $this->error_str = "Unexpected HTTP return code: $return_code"; return array($return_code,$this->error_str); } return array($return_code,$this->response_reason); } function put_object(&$obj, &$fp) { if (!is_object($obj) || get_class($obj) != "CF_Object") { throw new SyntaxException( "Method argument is not a valid CF_Object."); } if (!is_resource($fp)) { throw new SyntaxException( "File pointer argument is not a valid resource."); } $conn_type = "PUT_OBJ"; $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name); $hdrs = $this->_metadata_headers($obj); if (isset($etag)) { $hdrs[] = "ETag: " . $etag; } if (!$obj->content_type) { $hdrs[] = "Content-Type: application/octet-stream"; } else { $hdrs[] = "Content-Type: " . $obj->content_type; } $this->_init($conn_type); curl_setopt($this->connections[$conn_type], CURLOPT_INFILE, $fp); if (!$obj->content_length) { curl_setopt($this->connections[$conn_type], CURLOPT_UPLOAD, True); $hdrs[] = 'Transfer-Encoding: chunked'; } else { curl_setopt($this->connections[$conn_type], CURLOPT_INFILESIZE, $obj->content_length); } $return_code = $this->_send_request($conn_type,$url_path,$hdrs); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return array(0,$this->error_str,NULL); } if ($return_code == 412) { $this->error_str = "Missing Content-Type header"; return array(0,$this->error_str,NULL); } if ($return_code == 422) { $this->error_str = "Derived and computed checksums do not match."; return array(0,$this->error_str,NULL); } if ($return_code != 201) { $this->error_str = "Unexpected HTTP return code: $return_code"; return array(0,$this->error_str,NULL); } return array($return_code,$this->response_reason,$this->_obj_etag); } function update_object(&$obj) { if (!is_object($obj) || get_class($obj) != "CF_Object") { throw new SyntaxException( "Method argument is not a valid CF_Object."); } if (!is_array($obj->metadata) || empty($obj->metadata)) { $this->error_str = "Metadata array is empty."; return 0; } $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name); $hdrs = $this->_metadata_headers($obj); $return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST"); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return 0; } if ($return_code == 404) { $this->error_str = "Account, Container, or Object not found."; } if ($return_code != 202) { $this->error_str = "Unexpected HTTP return code: $return_code"; } return $return_code; } function head_object(&$obj) { if (!is_object($obj) || get_class($obj) != "CF_Object") { throw new SyntaxException( "Method argument is not a valid CF_Object."); } $conn_type = "HEAD"; $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name); $return_code = $this->_send_request($conn_type,$url_path); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return array(0, $this->error_str." ".$this->response_reason, NULL, NULL, NULL, NULL, array()); } if ($return_code == 404) { return array($return_code, $this->response_reason, NULL, NULL, NULL, NULL, array()); } if ($return_code == 204) { return array($return_code,$this->response_reason, $this->_obj_etag, $this->_obj_last_modified, $this->_obj_content_type, $this->_obj_content_length, $this->_obj_metadata); } $this->error_str = "Unexpected HTTP return code: $return_code"; return array($return_code, $this->error_str." ".$this->response_reason, NULL, NULL, NULL, NULL, array()); } function delete_object($container_name, $object_name) { if (!$container_name || !$object_name) { $this->error_str = "Container or Object name not set."; return 0; } $url_path = $this->_make_path("STORAGE", $container_name,$object_name); $return_code = $this->_send_request("DEL_POST",$url_path,NULL,"DELETE"); if (!$return_code) { $this->error_str = "Failed to obtain http response"; return 0; } if ($return_code == 404) { $this->error_str = "Specified container did not exist to delete."; } if ($return_code != 204) { $this->error_str = "Unexpected HTTP return code: $return_code."; } return $return_code; } function get_error() { return $this->error_str; } function setDebug($bool) { $this->dbug = $bool; foreach ($this->connections as $k => $v) { if (!is_null($v)) { curl_setopt($this->connections[$k], CURLOPT_VERBOSE, $this->dbug); } } } function getCDNMUrl() { return $this->cdnm_url; } function getStorageUrl() { return $this->storage_url; } function getAuthToken() { return $this->auth_token; } function setCFAuth($cfs_auth) { $this->storage_url = $cfs_auth->storage_url; $this->auth_token = $cfs_auth->auth_token; $this->cdnm_url = $cfs_auth->cdnm_url; } function setReadProgressFunc($func_name) { $this->_user_read_progress_callback_func = $func_name; } function setWriteProgressFunc($func_name) { $this->_user_write_progress_callback_func = $func_name; } private function _header_cb($ch, $header) { preg_match("/^HTTP\/1\.[01] (\d{3}) (.*)/", $header, $matches); if (isset($matches[1])) { $this->response_status = $matches[1]; } if (isset($matches[2])) { $this->response_reason = $matches[2]; } if (stripos($header, CDN_ENABLED) === 0) { $val = trim(substr($header, strlen(CDN_ENABLED)+1)); if (strtolower($val) == "true") { $this->_cdn_enabled = True; } elseif (strtolower($val) == "false") { $this->_cdn_enabled = False; } else { $this->_cdn_enabled = NULL; } return strlen($header); } if (stripos($header, CDN_URI) === 0) { $this->_cdn_uri = trim(substr($header, strlen(CDN_URI)+1)); return strlen($header); } if (stripos($header, CDN_TTL) === 0) { $this->_cdn_ttl = trim(substr($header, strlen(CDN_TTL)+1))+0; return strlen($header); } if (stripos($header, ACCOUNT_CONTAINER_COUNT) === 0) { $this->_account_container_count = trim(substr($header, strlen(ACCOUNT_CONTAINER_COUNT)+1))+0; return strlen($header); } if (stripos($header, ACCOUNT_BYTES_USED) === 0) { $this->_account_bytes_used = trim(substr($header, strlen(ACCOUNT_BYTES_USED)+1))+0; return strlen($header); } if (stripos($header, CONTAINER_OBJ_COUNT) === 0) { $this->_container_object_count = trim(substr($header, strlen(CONTAINER_OBJ_COUNT)+1))+0; return strlen($header); } if (stripos($header, CONTAINER_BYTES_USED) === 0) { $this->_container_bytes_used = trim(substr($header, strlen(CONTAINER_BYTES_USED)+1))+0; return strlen($header); } if (stripos($header, METADATA_HEADER) === 0) { $temp = substr($header, strlen(METADATA_HEADER)); $parts = explode(":", $temp); $this->_obj_metadata[strtolower($parts[0])] = trim($parts[1]); return strlen($header); } if (stripos($header, "ETag") === 0) { $parts = explode(":", $header); $this->_obj_etag = trim($parts[1]); return strlen($header); } if (stripos($header, "Last-Modified") === 0) { $parts = explode(":", $header); $this->_obj_last_modified = trim($parts[1]); return strlen($header); } if (stripos($header, "Content-Type") === 0) { $parts = explode(":", $header); $this->_obj_content_type = trim($parts[1]); return strlen($header); } if (stripos($header, "Content-Length") === 0) { $parts = explode(":", $header); $this->_obj_content_length = trim($parts[1])+0; return strlen($header); } return strlen($header); } private function _read_cb($ch, $fd, $length) { $data = fread($fd, $length); $len = strlen($data); if (isset($this->_user_write_progress_callback_func)) { call_user_func($this->_user_write_progress_callback_func, $len); } return $data; } private function _write_cb($ch, $data) { $amount = strlen($data); switch ($this->_write_callback_type) { case "TEXT_LIST": $this->_text_list[] = rtrim($data, "\r\n\0\x0B"); break; case "OBJECT_STREAM": $written = 0; while ($written < strlen($data)) { $written += fwrite($this->_obj_write_resource, substr($data, $written)); } if ($written > strlen($data)) { throw new IOException( "Wrote more data to client than we should have?!"); } break; case "OBJECT_STRING": $this->_obj_write_string .= $data; break; } if (isset($this->_user_read_progress_callback_func)) { call_user_func($this->_user_read_progress_callback_func,strlen($data)); } return strlen($data); } private function _auth_hdr_cb($ch, $header) { preg_match("/^HTTP\/1\.[01] (\d{3}) (.*)/", $header, $matches); if (isset($matches[1])) { $this->response_status = $matches[1]; } if (isset($matches[2])) { $this->response_reason = $matches[2]; } if (stripos($header, STORAGE_URL) === 0) { $this->storage_url = trim(substr($header, strlen(STORAGE_URL)+1)); } if (stripos($header, CDNM_URL) === 0) { $this->cdnm_url = trim(substr($header, strlen(CDNM_URL)+1)); } if (stripos($header, AUTH_TOKEN) === 0) { $this->auth_token = trim(substr($header, strlen(AUTH_TOKEN)+1)); } if (stripos($header, AUTH_TOKEN_LEGACY) === 0) { $this->auth_token = trim(substr($header,strlen(AUTH_TOKEN_LEGACY)+1)); } return strlen($header); } private function _make_headers($hdrs=NULL) { $new_headers = array(); $has_stoken = False; $has_uagent = False; if (is_array($hdrs)) { foreach ($hdrs as $h => $v) { if (is_int($h)) { $parts = explode(":", $v); $header = $parts[0]; $value = trim($parts[1]); } else { $header = $h; $value = trim($v); } if (stripos($header, AUTH_TOKEN) === 0) { $has_stoken = True; } if (stripos($header, "user-agent") === 0) { $has_uagent = True; } $new_headers[] = $header . ": " . $value; } } if (!$has_stoken) { $new_headers[] = AUTH_TOKEN . ": " . $this->auth_token; } if (!$has_uagent) { $new_headers[] = "User-Agent: " . USER_AGENT; } return $new_headers; } private function _init($conn_type, $force_new=False) { if (!array_key_exists($conn_type, $this->connections)) { $this->error_str = "Invalid CURL_XXX connection type"; return False; } if (is_null($this->connections[$conn_type]) || $force_new) { $ch = curl_init(); } else { return; } $this->_set_connections_cainfo(); if ($this->dbug) { curl_setopt($ch, CURLOPT_VERBOSE, 1); } curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_MAXREDIRS, 4); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this, '_header_cb')); if ($conn_type == "GET_CALL") { curl_setopt($ch, CURLOPT_WRITEFUNCTION, array(&$this, '_write_cb')); } if ($conn_type == "PUT_OBJ") { curl_setopt($ch, CURLOPT_PUT, 1); curl_setopt($ch, CURLOPT_READFUNCTION, array(&$this, '_read_cb')); } if ($conn_type == "HEAD") { curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "HEAD"); curl_setopt($ch, CURLOPT_NOBODY, 1); } if ($conn_type == "PUT_CONT") { curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); curl_setopt($ch, CURLOPT_INFILESIZE, 0); } if ($conn_type == "DEL_POST") { curl_setopt($ch, CURLOPT_NOBODY, 1); } $this->connections[$conn_type] = $ch; return; } private function _reset_callback_vars() { $this->_text_list = array(); $this->_account_container_count = 0; $this->_account_bytes_used = 0; $this->_container_object_count = 0; $this->_container_bytes_used = 0; $this->_obj_etag = NULL; $this->_obj_last_modified = NULL; $this->_obj_content_type = NULL; $this->_obj_content_length = NULL; $this->_obj_metadata = array(); $this->_obj_write_string = ""; $this->_cdn_enabled = NULL; $this->_cdn_uri = NULL; $this->_cdn_ttl = NULL; $this->response_status = 0; $this->response_reason = ""; } private function _make_path($t="STORAGE",$c=NULL,$o=NULL) { $path = array(); switch ($t) { case "STORAGE": $path[] = $this->storage_url; break; case "CDN": $path[] = $this->cdnm_url; break; } if ($c) { $path[] = rawurlencode($c); } if ($o) { $path[] = str_replace("%2F","/",rawurlencode($o)); } return implode("/",$path); } private function _metadata_headers(&$obj) { $hdrs = array(); foreach ($obj->metadata as $k => $v) { if (strpos($k,":") !== False || strpos($v,":") !== False) { throw new SyntaxException( "Metadata cannot contain a ':' character."); } $k = strtolower(trim($k)); $key = sprintf("%s%s", METADATA_HEADER, $k); if (!array_key_exists($key, $hdrs)) { if (strlen($k) > 128 || strlen($v) > 256) { $this->error_str = "Metadata key or value exceeds "; $this->error_str .= "maximum length: ($k: $v)"; return 0; } $hdrs[] = sprintf("%s%s: %s", METADATA_HEADER, $k, trim($v)); } } return $hdrs; } private function _send_request($conn_type, $url_path, $hdrs=NULL, $method="GET") { $this->_init($conn_type); $this->_reset_callback_vars(); $headers = $this->_make_headers($hdrs); switch ($method) { case "DELETE": curl_setopt($this->connections[$conn_type], CURLOPT_CUSTOMREQUEST, "DELETE"); break; case "POST": curl_setopt($this->connections[$conn_type], CURLOPT_CUSTOMREQUEST, "POST"); default: break; } curl_setopt($this->connections[$conn_type], CURLOPT_HTTPHEADER, $headers); curl_setopt($this->connections[$conn_type], CURLOPT_URL, $url_path); if (!curl_exec($this->connections[$conn_type])) { $this->error_str = "(curl error: " . curl_errno($this->connections[$conn_type]) . ") "; $this->error_str .= curl_error($this->connections[$conn_type]); return False; } return curl_getinfo($this->connections[$conn_type], CURLINFO_HTTP_CODE); } }
 
 
define("DEFAULT_CF_API_VERSION", 1); define("MAX_CONTAINER_NAME_LEN", 256); define("MAX_OBJECT_NAME_LEN", 1024); define("MAX_OBJECT_SIZE", 5*1024*1024*1024+1); class CF_Authentication { public $dbug; public $username; public $api_key; public $auth_host; public $account; public $storage_url; public $cdnm_url; public $auth_token; function __construct($username, $api_key, $account=NULL, $auth_host=NULL) { if (!$username || !$api_key) { throw new SyntaxException("Missing required constructor arguments."); } $this->dbug = False; $this->username = $username; $this->api_key = $api_key; $this->account_name = $account; $this->auth_host = $auth_host; $this->storage_url = NULL; $this->cdnm_url = NULL; $this->auth_token = NULL; $this->cfs_http = new CF_Http(DEFAULT_CF_API_VERSION); } function ssl_use_cabundle($path=NULL) { $this->cfs_http->ssl_use_cabundle($path); } function authenticate($version=DEFAULT_CF_API_VERSION) { list($status,$reason,$surl,$curl,$atoken) = $this->cfs_http->authenticate($this->username, $this->api_key, $this->account_name, $this->auth_host); if ($status == 401) { throw new AuthenticationException("Invalid username or access key."); } if ($status != 204) { throw new InvalidResponseException( "Unexpected response (".$status."): ".$reason); } if (!($surl || $curl) || !$atoken) { throw new InvalidResponseException( "Expected headers missing from auth service."); } $this->storage_url = $surl; $this->cdnm_url = $curl; $this->auth_token = $atoken; return True; } function authenticated() { if (!($this->storage_url || $this->cdnm_url) || !$this->auth_token) { return False; } return True; } function setDebug($bool) { $this->dbug = $bool; $this->cfs_http->setDebug($bool); } } class CF_Connection { public $dbug; public $cfs_http; public $cfs_auth; function __construct($cfs_auth) { $this->cfs_http = new CF_Http(DEFAULT_CF_API_VERSION); $this->cfs_auth = $cfs_auth; if (!$this->cfs_auth->authenticated()) { $e = "Need to pass in a previously authenticated "; $e .= "CF_Authentication instance."; throw new AuthenticationException($e); } $this->cfs_http->setCFAuth($this->cfs_auth); $this->dbug = False; } function setDebug($bool) { $this->dbug = (boolean) $bool; $this->cfs_http->setDebug($this->dbug); } function get_info() { list($status, $reason, $container_count, $total_bytes) = $this->cfs_http->head_account(); if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } return array($container_count, $total_bytes); } function create_container($container_name=NULL) { if (!$container_name) { throw new SyntaxException("Container name not set."); } if (strpos($container_name, "/") !== False) { $r = "Container name '".$container_name; $r .= "' cannot contain a '/' character."; throw new SyntaxException($r); } if (strpos($container_name, "?") !== False) { $r = "Container name '".$container_name; $r .= "' cannot contain a '?' character."; throw new SyntaxException($r); } if (mb_strlen($container_name, "UTF-8") > MAX_CONTAINER_NAME_LEN) { throw new SyntaxException(sprintf( "Container name exeeds %d characters.", MAX_CONTAINER_NAME_LEN)); } $return_code = $this->cfs_http->create_container($container_name); if (!$return_code) { throw new InvalidResponseException("Invalid response (" . $return_code. "): " . $this->cfs_http->get_error()); } if ($return_code != 201 && $return_code != 202) { throw new InvalidResponseException( "Invalid response (".$return_code."): " . $this->cfs_http->get_error()); } return new CF_Container($this->cfs_auth, $this->cfs_http, $container_name); } function delete_container($container=NULL) { if (is_object($container)) { if (get_class($container) == "CF_Container") { $container_name = $container->name; } } if (is_string($container)) { $container_name = $container; } if (!$container_name) { throw new SyntaxException("Must specify container object or name."); } $return_code = $this->cfs_http->delete_container($container_name); if (!$return_code) { throw new InvalidResponseException("Failed to obtain http response"); } if ($return_code == 409) { throw new NonEmptyContainerException( "Container must be empty prior to removing it."); } if ($return_code == 404) { throw new NoSuchContainerException( "Specified container did not exist to delete."); } if ($return_code != 204) { throw new InvalidResponseException( "Invalid response (".$return_code."): " . $this->cfs_http->get_error()); } return True; } function get_container($container_name=NULL) { list($status, $reason, $count, $bytes) = $this->cfs_http->head_container($container_name); if ($status == 404) { throw new NoSuchContainerException("Container not found."); } if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response: ".$this->cfs_http->get_error()); } return new CF_Container($this->cfs_auth, $this->cfs_http, $container_name, $count, $bytes); } function get_containers() { list($status, $reason, $container_info) = $this->cfs_http->list_containers_info(); if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response: ".$this->cfs_http->get_error()); } $containers = array(); foreach ($container_info as $name => $info) { $containers[] = new CF_Container($this->cfs_auth, $this->cfs_http, $name, $info["count"], $info["bytes"], False); } return $containers; } function list_containers($limit=0, $marker=NULL) { list($status, $reason, $containers) = $this->cfs_http->list_containers($limit, $marker); if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } return $containers; } function list_containers_info($limit=0, $marker=NULL) { list($status, $reason, $container_info) = $this->cfs_http->list_containers_info($limit, $marker); if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } return $container_info; } function list_public_containers() { list($status, $reason, $containers) = $this->cfs_http->list_cdn_containers(); if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } return $containers; } function set_read_progress_function($func_name) { $this->cfs_http->setReadProgressFunc($func_name); } function set_write_progress_function($func_name) { $this->cfs_http->setWriteProgressFunc($func_name); } function ssl_use_cabundle($path=NULL) { $this->cfs_http->ssl_use_cabundle($path); } } class CF_Container { public $cfs_auth; public $cfs_http; public $name; public $object_count; public $bytes_used; public $cdn_enabled; public $cdn_uri; public $cdn_ttl; function __construct(&$cfs_auth, &$cfs_http, $name, $count=0, $bytes=0, $docdn=True) { if (mb_strlen($name, "UTF-8") > MAX_CONTAINER_NAME_LEN) { throw new SyntaxException("Container name exceeds " . "maximum allowed length."); } if (strpos($name, "/") !== False) { throw new SyntaxException( "Container names cannot contain a '/' character."); } $this->cfs_auth = $cfs_auth; $this->cfs_http = $cfs_http; $this->name = $name; $this->object_count = $count; $this->bytes_used = $bytes; $this->cdn_enabled = NULL; $this->cdn_uri = NULL; $this->cdn_ttl = NULL; if ($this->cfs_http->getCDNMUrl() != NULL && $docdn) { $this->_cdn_initialize(); } } function __toString() { $me = sprintf("name: %s, count: %.0f, bytes: %.0f", $this->name, $this->object_count, $this->bytes_used); if ($this->cfs_http->getCDNMUrl() != NULL) { $me .= sprintf(", cdn: %s, cdn uri: %s, cdn ttl: %.0f", $this->is_public() ? "Yes" : "No", $this->cdn_uri, $this->cdn_ttl); } return $me; } function make_public($ttl=86400) { if ($this->cfs_http->getCDNMUrl() == NULL) { throw new CDNNotEnabledException( "Authentication response did not indicate CDN availability"); } if ($this->cdn_uri != NULL) { list($status, $reason, $cdn_uri) = $this->cfs_http->update_cdn_container($this->name,$ttl); if ($status == 404) { list($status, $reason, $cdn_uri) = $this->cfs_http->add_cdn_container($this->name,$ttl); } } else { list($status, $reason, $cdn_uri) = $this->cfs_http->add_cdn_container($this->name,$ttl); } if (!in_array($status, array(201,202))) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } $this->cdn_enabled = True; $this->cdn_ttl = $ttl; $this->cdn_uri = $cdn_uri; return $this->cdn_uri; } function make_private() { if ($this->cfs_http->getCDNMUrl() == NULL) { throw new CDNNotEnabledException( "Authentication response did not indicate CDN availability"); } list($status,$reason) = $this->cfs_http->remove_cdn_container($this->name); if (!in_array($status, array(202,404))) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } $this->cdn_enabled = False; $this->cdn_ttl = NULL; $this->cdn_uri = NULL; return True; } function is_public() { return $this->cdn_enabled == True ? True : False; } function create_object($obj_name=NULL) { return new CF_Object($this, $obj_name); } function get_object($obj_name=NULL) { return new CF_Object($this, $obj_name, True); } function list_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL) { list($status, $reason, $obj_list) = $this->cfs_http->list_objects($this->name, $limit, $marker, $prefix, $path); if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } return $obj_list; } function get_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL) { list($status, $reason, $obj_array) = $this->cfs_http->get_objects($this->name, $limit, $marker, $prefix, $path); if ($status < 200 || $status > 299) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } $objects = array(); foreach ($obj_array as $obj) { $tmp = new CF_Object($this, $obj["name"], False, False); $tmp->content_type = $obj["content_type"]; $tmp->content_length = $obj["bytes"]; $tmp->set_etag($obj["hash"]); $objects[] = $tmp; } return $objects; } function delete_object($obj) { $obj_name = NULL; if (is_object($obj)) { if (get_class($obj) == "CF_Object") { $obj_name = $obj->name; } } if (is_string($obj)) { $obj_name = $obj; } if (!$obj_name) { throw new SyntaxException("Object name not set."); } $status = $this->cfs_http->delete_object($this->name, $obj_name); if ($status == 404) { $m = "Specified object '".$this->name."/".$obj_name; $m.= "' did not exist to delete."; throw new NoSuchObjectException($m); } if ($status != 204) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } return True; } function create_paths($path_name) { if ($path_name[0] == '/') { $path_name = mb_substr($path_name, 1); } $elements = explode('/', $path_name, -1); $build_path = ""; foreach ($elements as $idx => $val) { if (!$build_path) { $build_path = $val; } else { $build_path .= "/" . $val; } $obj = new CF_Object($this, $build_path); $obj->content_type = "application/directory"; $obj->write(".", 1); } } private function _cdn_initialize() { list($status, $reason, $cdn_enabled, $cdn_uri, $cdn_ttl) = $this->cfs_http->head_cdn_container($this->name); if (!in_array($status, array(204,404))) { throw new InvalidResponseException( "Invalid response (".$status."): ".$this->cfs_http->get_error()); } $this->cdn_enabled = $cdn_enabled; $this->cdn_uri = $cdn_uri; $this->cdn_ttl = $cdn_ttl; } } class CF_Object { public $container; public $name; public $last_modified; public $content_type; public $content_length; public $metadata; private $etag; function __construct(&$container, $name, $force_exists=False, $dohead=True) { if ($name[0] == "/") { $r = "Object name '".$name; $r .= "' cannot contain begin with a '/' character."; throw new SyntaxException($r); } if (mb_strlen($name, "UTF-8") > MAX_OBJECT_NAME_LEN) { throw new SyntaxException("Object name exceeds " . "maximum allowed length."); } $this->container = $container; $this->name = $name; $this->etag = NULL; $this->_etag_override = False; $this->last_modified = NULL; $this->content_type = NULL; $this->content_length = 0; $this->metadata = array(); if ($dohead) { if (!$this->_initialize() && $force_exists) { throw new NoSuchObjectException("No such object '".$name."'"); } } } function __toString() { return $this->container->name . "/" . $this->name; } function public_uri() { if ($this->container->cdn_enabled) { return $this->container->cdn_uri . "/" . $this->name; } return NULL; } function read($hdrs=array()) { list($status, $reason, $data) = $this->container->cfs_http->get_object_to_string($this, $hdrs); if (($status < 200) || ($status > 299 && $status != 412 && $status != 304)) { throw new InvalidResponseException("Invalid response (".$status."): " . $this->container->cfs_http->get_error()); } return $data; } function stream(&$fp, $hdrs=array()) { list($status, $reason) = $this->container->cfs_http->get_object_to_stream($this,$fp,$hdrs); if (($status < 200) || ($status > 299 && $status != 412 && $status != 304)) { throw new InvalidResponseException("Invalid response (".$status."): " .$reason); } return True; } function sync_metadata() { if (!empty($this->metadata)) { $status = $this->container->cfs_http->update_object($this); if ($status != 202) { throw new InvalidResponseException("Invalid response (" .$status."): ".$this->container->cfs_http->get_error()); } return True; } return False; } function write($data=NULL, $bytes=0, $verify=True) { if (!$data) { throw new SyntaxException("Missing data source."); } if ($bytes > MAX_OBJECT_SIZE) { throw new SyntaxException("Bytes exceeds maximum object size."); } if ($verify) { if (!$this->_etag_override) { $this->etag = $this->compute_md5sum($data); } } else { $this->etag = NULL; } $close_fh = False; if (!is_resource($data)) { $fp = fopen("php://memory", "rb+"); fwrite($fp, $data); rewind($fp); $close_fh = True; $this->content_length = (float) mb_strlen($data); if ($this->content_length > MAX_OBJECT_SIZE) { throw new SyntaxException("Data exceeds maximum object size"); } } else { $this->content_length = $bytes; $fp = $data; } if (!$this->content_type) { if (function_exists("finfo_open")) { $finfo = False; try { $finfo = @finfo_open(FILEINFO_MIME); } catch (Exception $e) { try { $my_mime = dirname(__FILE__) . "/magic"; $finfo = @finfo_open(FILEINFO_MIME, $my_mime); } catch (Exception $se) { $finfo = False; } } if (!$finfo) { $this->content_type = "application/octet-stream"; } else { $ct = finfo_buffer($finfo, $data); finfo_close($finfo); if (!$ct) { $this->content_type = "application/octet-stream"; } else { $this->content_type = $ct; } } } else { $this->content_type = "application/octet-stream"; } } list($status, $reason, $etag) = $this->container->cfs_http->put_object($this, $fp); if ($status == 412) { if ($close_fh) { fclose($fp); } throw new SyntaxException("Missing Content-Type header"); } if ($status == 422) { if ($close_fh) { fclose($fp); } throw new MisMatchedChecksumException( "Supplied and computed checksums do not match."); } if ($status != 201) { if ($close_fh) { fclose($fp); } throw new InvalidResponseException("Invalid response (".$status."): " . $this->container->cfs_http->get_error()); } if (!$verify) { $this->etag = $etag; } if ($close_fh) { fclose($fp); } return True; } function load_from_filename($filename, $verify=True) { $fp = @fopen($filename, "r"); if (!$fp) { throw new IOException("Could not open file for reading: ".$filename); } $size = (float) sprintf("%u", filesize($filename)); if ($size > MAX_OBJECT_SIZE) { throw new SyntaxException("File size exceeds maximum object size."); } $auto_ct = False; if (function_exists("finfo_open")) { $finfo = False; try { $finfo = @finfo_open(FILEINFO_MIME); } catch (Exception $e) { try { $my_mime = dirname(__FILE__) . "/magic"; $finfo = @finfo_open(FILEINFO_MIME, $my_mime); } catch (Exception $se) { $finfo = False; } } if ($finfo) { $ct = finfo_file($finfo, $filename); finfo_close($finfo); if ($ct) { $this->content_type = $ct; $auto_ct = True; } } } if (!$auto_ct && function_exists("mime_content_type")) { $this->content_type = @mime_content_type($filename); } else { $this->content_type = "application/octet-stream"; } $this->write($fp, $size, $verify); fclose($fp); return True; } function save_to_filename($filename) { $fp = @fopen($filename, "wb"); if (!$fp) { throw new IOException("Could not open file for writing: ".$filename); } $result = $this->stream($fp); fclose($fp); return $result; } function set_etag($etag) { $this->etag = $etag; $this->_etag_override = True; } function getETag() { return $this->etag; } function compute_md5sum(&$data) { if (is_file($data)) { $md5 = md5_file($data); } elseif (is_resource($data)) { $contents = stream_get_contents($data); $md5 = md5($contents); rewind($data); } else { $md5 = md5($data); } return $md5; } private function _initialize() { list($status, $reason, $etag, $last_modified, $content_type, $content_length, $metadata) = $this->container->cfs_http->head_object($this); if ($status == 404) { return False; } if ($status < 200 || $status > 299) { throw new InvalidResponseException("Invalid response (".$status."): " . $this->container->cfs_http->get_error()); } $this->etag = $etag; $this->last_modified = $last_modified; $this->content_type = $content_type; $this->content_length = $content_length; $this->metadata = $metadata; return True; } }

/* END CLOUD FILES LIBRARIES */

/* ! FUNCTIONS */
function isFlag($arg) { // check if passed argument is a flag (starts with -). returns true/false;
	$test = stripos($arg, "-");
	if($test === false) {
		return false;
	} elseif($test == 0) {
		return true;
	} else {
		return false;
	}
}

function downloadDir() {
	global $bckp, $directory, $verbose;

	$list = $bckp->list_objects(0, null, null);
	
	if(empty($list)) {
		return false;
	}
	
	
	foreach($list as $tmp) {
		if(! file_exists($directory.'/'.$tmp)) {
			$obj = $bckp->get_object($tmp);
			
			try {
				$obj->save_to_filename($directory.'/'.$tmp);
				$msg = "Object downloaded: $tmp \n";
			} catch(Exception $e) {
				$msg = "Object could not be downloaded: $tmp \n";
			}
			
			
			if($verbose) {
				echo $msg;
			}
		}
	}
	return true;
}

function parseContainer($arg) { // parse container and set variables. returns false on error, true on success
	global $container, $key, $username;

	// split :. should get two parts
	$test = explode(":",$arg);
	
	if(count($test) != 2) {
		return false;
	}
	
	// split first section at '@'. should get two parts
	$test2 = explode("@", $test[0]);
	
	if(count($test2) != 2) {
		return false;
	}
	
	$container = $test[1];
	$username = $test2[0];
	$key = $test2[1];

	return true;
}

function parseFlag($arg) { // parse flags and set variables. returns false on error, true on success
	global $delete, $verbose, $recursive, $help, $download;
	$split = str_split($arg);
	if($split == false) {
		return false;
	}
	
	foreach($split as $char) {
		if($char == "d") {
			$delete = true;
		}
		
		if($char == "v") {
			$verbose = true;
		}
		
		if($char == "r") {
			$recursive = true;
		}
		
		if($char == "h") {
			$help = true;
		}
		
		if($char == "b") {
			$download = true;
		}
	}
	
	return true;
}

function removeFirstSlash($dir) { // remove first slash from directory
	if (substr($dir, 0, 1) == "/") {
		$dir = substr($dir, 1);
	}
	return $dir;
}

function syncFile($dir, $file) {
	global $directory, $bckp, $verbose;

	// local hash
	$md5Local = md5_file($directory.'/'.$dir.'/'.$file);
	
	// remote hash
	try {
		$obj = $bckp->get_object(removeFirstSlash($dir."/".$file));

		$md5Remote = $obj->getETag();
		$msg = "Object updated: ".removeFirstSlash($dir."/".$file);
	} catch(Exception $e) {
		$md5Remote = false;
		$obj = $bckp->create_object(removeFirstSlash($dir."/".$file));
		
		$msg = "Object added: ".removeFirstSlash($dir."/".$file);
	}
	
	if($md5Remote != $md5Local) {
		$obj->load_from_filename($directory.'/'.$dir.'/'.$file);

		if($verbose) {
			echo $msg."\n";
		}
	}
}

function syncDir($dir, $recursive) { // sync the directory.
	global $directory, $bckp;

	$dirHandle = dir($directory.'/'.$dir);
	while($file = $dirHandle->read()) {
		// upload files:
		if((! is_dir($dirHandle->path."/".$file))) {
			syncFile($dir, $file);
		} elseif (is_dir($dirHandle->path."/".$file) && $recursive && ($file != ".") && ($file != "..")) {
			if($recursive) {
				$bckp->create_paths(removeFirstSlash($dir.'/'.$file.'/'));
				syncDir($dir.'/'.$file, $recursive);
			}
		}
	}
}

function deleteOld() { // go through and delete old files
	global $directory, $bckp, $verbose;
	
	$list = $bckp->list_objects(0, null, null);
	
	if(empty($list)) {
		return false;
	}
	
	foreach($list as $tmp) {
		if(! file_exists($directory.'/'.$tmp)) {
			$obj = $bckp->get_object($tmp);
			$bckp->delete_object($obj);
			if($verbose) {
				echo "Object deleted: $tmp \n";
			}
		}
	}
	
	return true;
}

// ! check parameters and set actions
$delete = false; // -d
$verbose = false; // -v
$recursive = false; // -r

$username = false;
$container = false;
$key = false;
$directory = false;
$help = false;

$i = 1; // only increment if directory or container path. this is to keep track of it
$i2 = 0;
foreach($argv as $arg) {
	if($i2 != 0) {
		// check if is a flag
		if(isFlag($arg)) {
			parseFlag($arg);
		} else {
			if($i == 1) {
				$directory = $arg;
				$i++;
			} elseif ($i == 2) {
				parseContainer($arg);
				$i++;
			}
		}
	}
	$i2++;
}

if((($container == false) || ($key == false) || ($directory == false)) || ($help)) {
	die("USAGE FORMAT: \nphp cloudfiles-sync.php [-vdr] /directory username@accessKey:container


FLAGS	name		details
-v	verbose		output details, such as files created,
			updated, and deleted
-r	recursive	process directory recursively
-h	help	show this information
-b	download	download files from Cloud Files to directory
			-r (recursive) flag has no effect
			-d (delete) flag has no effect
-d	delete		delete files in Cloud Files that no longer
			exist in the directory. this is great for
			mirroring only. NOTE: the delete is recursive
			regardless, if the recursive (-r) flag is passed
			or not


NOTES
* Make sure php is installed with the Curl module
* Make sure php is compiled with mbstring support
* Files and folders have read access with the current user.
  You can use sudo, if needed
* You can run this script nightly using cron
* The /container/path will automatically be created if it does not exist.
* If downloading the files, destination directory must have write permissions
\n");
}

// remove trailing slash from directory
$len = strlen($directory)-1;

if($directory{$len} == "/") {
	$directory = substr($directory,0,$len);
}

// ! CONNECTION CHECKS + CORRECTIONS
// ! check connection / access id
$auth = new CF_Authentication($username, $key);
$auth->authenticate();
$conn = new CF_Connection($auth);

// ! add container
$bckp = $conn->create_container($container);

// ! PERFORM SYNC
if(! $download) {
	// ! Sync to Cloud Files
	$dir = @ dir($directory);
	
	if($dir == false) {
		die("Directory not found. \n");
	}
	
	$dir->close();
	
	syncDir(null, $recursive);
	
	if($delete) {
		deleteOld();
	}
} else {
	// ! Download files from Cloud Files
	
	$dir = @ dir($directory);
	
	if($dir == false) {
		die("Directory not found. Create the directory, and make sure it has read-write permissions to your user account. \n");
	}
	
	$dir->close();
	
	downloadDir();
}

// ! timer end
$__mtime = microtime();
$__mtime = explode(" ", $__mtime);
$__mtime = $__mtime[1] + $__mtime[0];
$__endtime = $__mtime;
$__totaltime = ($__endtime - $__starttime);
$__date = date(DATE_RFC822);
if($verbose) {
echo "\nSync Successful. \nDate: {$__date} \n";
echo $__totaltime." seconds \n\n";
}
?>